package com.fary.boot.context.properties.bind;

import com.fary.util.Assert;
import com.fary.util.ObjectUtils;

import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public final class BindResult<T> {

	// 没有绑定
    private static final BindResult<?> UNBOUND = new BindResult<>(null);

    // 绑定后的值
    private final T value;

    private BindResult(T value) {
        this.value = value;
    }

    /**
     * 获取绑定的值
     */
    public T get() throws NoSuchElementException {
        if (this.value == null) {
            throw new NoSuchElementException("No value bound");
        }
        return this.value;
    }

    /**
     * 是否绑定
     */
    public boolean isBound() {
        return (this.value != null);
    }

    /**
     * 对绑定后的值，进行处理
     */
    public void ifBound(Consumer<? super T> consumer) {
        Assert.notNull(consumer, "Consumer must not be null");
        if (this.value != null) {
            consumer.accept(this.value);
        }
    }

    /**
	 * mapper处理绑定后的值，重新返回 BindResult
     */
    public <U> BindResult<U> map(Function<? super T, ? extends U> mapper) {
        Assert.notNull(mapper, "Mapper must not be null");
        return of((this.value != null) ? mapper.apply(this.value) : null);
    }

    /**
	 * 没有绑定则返回 other
     */
    public T orElse(T other) {
        return (this.value != null) ? this.value : other;
    }

    /**
     * 没有绑定则返回 other的提供
     */
    public T orElseGet(Supplier<? extends T> other) {
        return (this.value != null) ? this.value : other.get();
    }

    /**
     * 没有绑定则返回 抛出异常
     */
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (this.value == null) {
            throw exceptionSupplier.get();
        }
        return this.value;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        return ObjectUtils.nullSafeEquals(this.value, ((BindResult<?>) obj).value);
    }

    @Override
    public int hashCode() {
        return ObjectUtils.nullSafeHashCode(this.value);
    }

    @SuppressWarnings("unchecked")
    static <T> BindResult<T> of(T value) {
        if (value == null) {
            return (BindResult<T>) UNBOUND;
        }
        return new BindResult<>(value);
    }

}